﻿namespace Blaze.Editor.LuaExtensions
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using EditorOnly;
    using Framework;
    using Framework.LuaExtensions;
    using UnityEditor;
    using UnityEditorInternal;
    using UnityEngine;

    /// <summary>
    /// 用于扩展<see cref="LuaBehaviour"/>在Inspector中的显示。
    /// </summary>
    [CustomEditor(typeof(LuaStateMachineBehaviour))]
    public class LuaStateMachineBehaviourInspector : Editor
    {
        static readonly string LUA_FILE_EXT = ".lua";

        class LuaRootInfo
        {
            public string Name;
            public string FullPath; //总是用/分割，不使用\。并且，结尾没有/ 。
        }

        class LuaFileInfo
        {
            public LuaRootInfo Root;
            public string[] Files;        //相对目录，没有.lua结尾
        }

        static IEnumerable<LuaRootInfo> s_GetLuaRoots()
        {
            string projectPath = Application.dataPath.Substring(0, Application.dataPath.Length - "/Assets".Length);
            yield return new LuaRootInfo() { Name = "Platform", FullPath = projectPath + "/Lua" };
        }

        static IEnumerable<LuaFileInfo> s_GetLuaFiles()
        {
            return s_GetLuaRoots().Select(Root =>
            {
                string[] Files = null;
                try
                {
                    Files = Directory.GetFiles(Root.FullPath, "*" + LUA_FILE_EXT, SearchOption.AllDirectories);
                }
                catch (Exception)
                {
                    return new LuaFileInfo() { Root = Root, Files = new string[0] };
                }

                Files = Files.Select(file =>
                {
                    file = file.Replace('\\', '/');
                    //remove root path
                    if (file.StartsWith(Root.FullPath, StringComparison.InvariantCultureIgnoreCase))
                        file = file.Substring(Root.FullPath.Length + 1);
                    else
                        return null;
                    //remove .lua
                    if (file.EndsWith(LUA_FILE_EXT, StringComparison.InvariantCultureIgnoreCase))
                        file = file.Substring(0, file.Length - LUA_FILE_EXT.Length);

                    return file;
                }).Where(x => x != null).ToArray();


                return new LuaFileInfo() { Root = Root, Files = Files };
            });
        }

        LuaRootInfo[] m_LuaRoots = null;
        LuaRootInfo[] GetLuaRoots()
        {
            if (m_LuaRoots == null)
                m_LuaRoots = s_GetLuaRoots().ToArray();
            return m_LuaRoots;
        }

        LuaFileInfo[] m_LuaFiles = null;
        LuaFileInfo[] GetLuaFiles()
        {
            if (m_LuaFiles == null)
                m_LuaFiles = s_GetLuaFiles().ToArray();
            return m_LuaFiles;
        }

        private void ShowLuaFilesDropDownMenu()
        {
            var m = new GenericMenu();
            foreach (var root in GetLuaFiles())
            {
                foreach (var file in root.Files)
                {
                    m.AddItem(new GUIContent(string.Format("[{0}]/{1}", root.Root.Name, file)), file == mBehaviour.LuaFilePath, () =>
                    {
                        //Debug.Log("select: " + root.Root.FullPath + "/" + file + ".lua");
                        if (mBehaviour != null && mBehaviour.LuaFilePath != file)
                        {
                            Undo.RecordObject(mBehaviour, "修改了lua文件路径");
                            mBehaviour.LuaFilePath = file;
                        }
                    });
                }
            }
            m.ShowAsContext();
        }

        GUIStyle m_DropZoneStyle;
        //如果obj是一个Texture2D asset，则尝试转换成一个Sprite
        static void TryConvertToSpriteAsset(ref UnityEngine.Object obj)
        {
            if (obj is Texture2D)
            {
                string path = AssetDatabase.GetAssetPath(obj);
                if (!string.IsNullOrEmpty(path))
                {
                    //当一个texture2d只包含一个sprite的时候，才转换成sprite
                    //否则认为你要拖动的就是一个texture2d
                    var objs = AssetDatabase.LoadAllAssetsAtPath(path);
                    int spriteCount = objs.Count(x => x is Sprite);
                    if (spriteCount == 1)
                    {
                        obj = objs.First(x => x is Sprite);
                    }
                }
            }
        }
        private void ShowDropZone()
        {
            float RECT_HEIGHT = 30.0f;
            var content = new GUIContent("把东西拖到这里可以一次性添加好多东西");
            int controlID = GUIUtility.GetControlID(998654321, FocusType.Passive);
            Rect rect = GUILayoutUtility.GetRect(content, GUI.skin.box, GUILayout.ExpandWidth(true), GUILayout.Height(RECT_HEIGHT));

            var eventType = Event.current.type;
            if (m_DropZoneStyle == null)
            {
                m_DropZoneStyle = new GUIStyle(GUI.skin.box);
                m_DropZoneStyle.alignment = TextAnchor.MiddleCenter;
            }
            switch (eventType)
            {
                case EventType.DragUpdated:
                case EventType.DragPerform:
                    if (rect.Contains(Event.current.mousePosition))
                    {
                        DragAndDrop.activeControlID = controlID;
                        DragAndDrop.visualMode = DragAndDropVisualMode.Copy;

                        if (eventType == EventType.DragPerform && DragAndDrop.objectReferences.Length > 0)
                        {
                            Undo.RecordObject(mBehaviour, "向LuaBehaviour添加了一堆东西");
                            foreach (var obj2 in DragAndDrop.objectReferences)
                            {
                                var obj = obj2;
                                string name = obj.name;
                                LuaFieldType type = EditorUtility.IsPersistent(obj) ? LuaFieldType.Asset : LuaFieldType.GameObject;
                                //如果拖入的object是一个Texture2D，并且只包含一个sprite，则可能想要拖入的是一个Sprite。
                                //自动转换成Sprite
                                if (type == LuaFieldType.Asset && obj is Texture2D)
                                {
                                    TryConvertToSpriteAsset(ref obj);
                                }

                                //if name is duplicatted, then create a good name.
                                if (mBehaviour.Fields.Any(x => x.K == name))
                                {
                                    name = ObjectNames.GetUniqueName(mBehaviour.Fields.Select(x => x.K).ToArray(), name);
                                }

                                mBehaviour.Fields.Add(new LuaFieldPair()
                                {
                                    K = name,
                                    T = type,
                                    V = obj
                                });
                            }
                        }
                        Event.current.Use();
                    }
                    break;
            }


            if (Event.current.type == EventType.Repaint)
            {
                var oldColor = GUI.color;
                if (DragAndDrop.activeControlID == controlID)
                {
                    content.text = "正在准备添加" + DragAndDrop.objectReferences.Length + "个东西";
                    //GUI.color = Color.gray;
                    m_DropZoneStyle.normal.textColor = Color.black;
                }
                else
                {
                    //GUI.color = Color.black;
                    m_DropZoneStyle.normal.textColor = Color.gray;
                }
                m_DropZoneStyle.Draw(rect, content, controlID);
                GUI.color = oldColor;
            }

        }

        private void drawFilePath()
        {
            EditorGUILayout.BeginHorizontal();
            GUILayout.Label("Lua File Path", GUILayout.Width(96));
            Color oldBackgroundColor = GUI.backgroundColor;
            GUILayout.BeginHorizontal();
            bool plusButtonClicked = GUILayout.Button("+", EditorStyles.miniButton, GUILayout.Width(20.0f));
            GUI.backgroundColor = isFileExist() ? Color.green : Color.red;
            string LuaFilePath = EditorGUILayout.TextField(mBehaviour.LuaFilePath);
            LuaFilePath = PathUtility.FormatSeperator(LuaFilePath);
            if (LuaFilePath != mBehaviour.LuaFilePath)
            {
                Undo.RecordObject(mBehaviour, "修改了lua文件路径");
                mBehaviour.LuaFilePath = LuaFilePath;
            }
            GUILayout.EndHorizontal();

            GUI.backgroundColor = oldBackgroundColor;
            if (plusButtonClicked)
            {
                ShowLuaFilesDropDownMenu();
            }

            GUI.backgroundColor = Color.white;
            if (GUILayout.Button("...", GUILayout.Width(32)))
            {
                var selection = EditorUtility.OpenFilePanelWithFilters("选择Lua文件", ProjectPath.Lua, new[] { "Lua", "lua" });
                if (!string.IsNullOrEmpty(selection))
                {
                    Undo.RecordObject(mBehaviour, "修改了lua文件路径");
                    selection = selection.Substring(ProjectPath.Root.Length + 1, selection.Length - ProjectPath.Root.Length - 5);
                    mBehaviour.LuaFilePath = PathUtility.FormatSeperator(selection);
                    Repaint();
                }
            }
            EditorGUILayout.EndHorizontal();
        }

        private bool hasDuplicatedField(string fieldName)
        {
            if (mBehaviour.Fields == null)
                return false;
            if (string.IsNullOrEmpty(fieldName))
                return true;

            var count = 0;
            for (var i = 0; i < mBehaviour.Fields.Count; i++)
            {
                if (mBehaviour.Fields[i].K == fieldName)
                {
                    if (count > 0)
                        return true;
                    count++;
                }
            }
            return false;
        }

        private bool isFileExist()
        {
            if (string.IsNullOrEmpty(mBehaviour.LuaFilePath))
                return false;
            foreach (var ri in GetLuaRoots())
            {
                if (File.Exists(ri.FullPath + "/" + mBehaviour.LuaFilePath + ".lua"))
                    return true;
            }
            return false;
        }

        private LuaStateMachineBehaviour mBehaviour;
        private ReorderableList mReorderableList;

        #region #Build-in methods

        public override void OnInspectorGUI()
        {
            mBehaviour = target as LuaStateMachineBehaviour;
            if (mBehaviour == null)
            {
                base.OnInspectorGUI();
                return;
            }
            drawFilePath();
            mBehaviour.OnInspectorGUI();
            ShowDropZone();
            if (mReorderableList != null)
                mReorderableList.DoLayoutList();
        }

        private void OnEnable()
        {
            mBehaviour = (LuaStateMachineBehaviour)target;
            mReorderableList = new ReorderableList(mBehaviour.Fields, typeof(LuaFieldPair), true, true, true, true)
            {
                drawHeaderCallback = onDrawHeader,
                drawElementCallback = onDrawElement,
                onAddCallback = onAddItem,
                onRemoveCallback = onRemoveItem
            };
        }

        private void OnDisable()
        {
            mReorderableList.drawHeaderCallback = null;
            mReorderableList.drawElementCallback = null;
            mReorderableList.onAddCallback = null;
            mReorderableList.onRemoveCallback = null;
            mReorderableList = null;
        }

        #endregion

        #region ReorderableList event handlers

        private void onRemoveItem(ReorderableList list)
        {
            Undo.RecordObject(target, "向LuaBehaviour删除了一个东西");
            mBehaviour.Fields.RemoveAt(list.index);
            EditorUtility.SetDirty(target);
        }

        private void onAddItem(ReorderableList list)
        {
            Undo.RecordObject(target, "向LuaBehaviour添加了一个东西");
            mBehaviour.Fields.Add((new LuaFieldPair
            {
                K = string.Empty,
                T = LuaFieldType.GameObject
            }));
            EditorUtility.SetDirty(target);
        }

        private void onDrawElement(Rect rect, int index, bool isactive, bool isfocused)
        {
            var fieldPair = mBehaviour.Fields[index];
            EditorGUI.BeginChangeCheck();

            const int padding = 4;

            var h = rect.height - padding;
            var x = rect.x;
            var y = rect.y + padding;

            var typeRect = new Rect(x, y, 60, h);
            x += typeRect.width + padding;

            float maxKeyWidth = Mathf.Max(10.0f, (rect.width - typeRect.width - padding - padding) * 0.5f);
            var keyRect = new Rect(x, y, 120, h);
            if (keyRect.width > maxKeyWidth)
                keyRect.width = maxKeyWidth;
            x += keyRect.width + padding;
            var valueRect = new Rect(x, y, rect.width - typeRect.width - keyRect.width - padding, h);

            var lastType = fieldPair.T;
            fieldPair.T = (LuaFieldType)EditorGUI.EnumPopup(typeRect, fieldPair.T);
            if (fieldPair.T != lastType)
                fieldPair.ClearValues();

            var originColor = GUI.backgroundColor;
            GUI.backgroundColor = hasDuplicatedField(fieldPair.K) ? Color.red : Color.green;

            switch (fieldPair.T)
            {
                case LuaFieldType.GameObject:
                    var k = EditorGUI.TextField(keyRect, fieldPair.K);
                    var v = EditorGUI.ObjectField(valueRect, fieldPair.V, typeof(GameObject), true);
                    if (string.IsNullOrEmpty(k) && v != null)
                        k = v.name;
                    fieldPair.K = k;
                    fieldPair.V = v;
                    break;
                case LuaFieldType.String:
                    fieldPair.K = EditorGUI.TextField(keyRect, fieldPair.K);
                    fieldPair.V1 = EditorGUI.TextField(valueRect, fieldPair.V1);
                    break;
                case LuaFieldType.Asset:
                    fieldPair.K = EditorGUI.TextField(keyRect, fieldPair.K);
                    fieldPair.V = EditorGUI.ObjectField(valueRect, fieldPair.V, typeof(UnityEngine.Object), false);
                    break;
                case LuaFieldType.Number:
                    fieldPair.K = EditorGUI.TextField(keyRect, fieldPair.K);
                    fieldPair.V2 = EditorGUI.FloatField(valueRect, fieldPair.V2);
                    break;
                case LuaFieldType.Boolean:
                    fieldPair.K = EditorGUI.TextField(keyRect, fieldPair.K);
                    var toggleResult = EditorGUI.Toggle(valueRect, fieldPair.V2 != 0);
                    fieldPair.V2 = toggleResult ? 1 : 0;
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }

            GUI.backgroundColor = originColor;

            if (EditorGUI.EndChangeCheck())
            {
                Undo.RecordObject(mBehaviour, "修改了LuaBehaviour的属性");
                mBehaviour.Fields[index] = fieldPair;
                EditorUtility.SetDirty(target);
            }
        }

        private void onDrawHeader(Rect rect)
        {
            GUI.Label(rect, "Lua Fields");
        }

        #endregion
    }
}